﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
//using System.Linq;
using System.Text;
using System.Windows.Forms;
using log4net;

using log4net.Repository.Hierarchy;
using RollBack.src.ui.ExplorerView;
using System.IO;
using RollBackLib.src.utils;
using RollBack.src.ui.Dialogs;
using RollBackLib.src.core;
using RollbackLib.src.utils;
using RollBackUI;

namespace RollBack.src.ui
{
    /// <summary>
    /// Encapsulate the 2 panel Filemanager and the bottom backup panel.
    /// </summary>
    public partial class ExplorerAndBackupJobs : UserControl
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(ExplorerAndBackupJobs));
        public BackupRestoreRunner m_backupRestoreRunner;
        private bool m_handleCreated = false;

        public ExplorerAndBackupJobs()
        {
            EmailAndSmtpTable.m_EmailTableFile = AppUI.getAppDataPath() + "/EmailTable.xml";
            EmailAndSmtpTable.m_SmtpTableFile = AppUI.getAppDataPath() + "/SmtpTable.xml";
            InitializeComponent();
            this.m_splitContainer.Panel2Collapsed = true;
            this.m_explorerTabControl.ImageList = ImageResources.m_resources.m_imageList;
            m_fileErrorsDialog = new FileErrorsDialog();

            m_backupRestoreRunner = new BackupRestoreRunner(this.statusUpdateHandler);
            m_backupSetComposite.m_backupRunner = m_backupRestoreRunner;

            BackupJobTabEventHandlers handlers = new BackupJobTabEventHandlers(this.startButtonClick,
                this.stopButtonClick,
                this.advancedButtonClick,
                this.dragDropHandler);
            m_backupSetComposite.setJobTabSelectionHandler(backupJobTabSelectionEventHandler);
            m_backupSetComposite.setEventHandler(handlers);
            m_backupRestoreRunner.startSchedulerThread();
        }

        /// <summary>
        /// Create the resotre, message viewer and job history tab.
        /// </summary>
        private void createOtherTabs()
        {
            // add the remaining tabs
            addLogMessageViewerTab();
            addRestoreTab();
            addJobHistoryTab();

            if (m_jobHistoryViewer != null)
            {
                String file = AppUI.getSettingsFile();
                m_jobHistoryViewer.loadHistoryData(file);
            }
            this.ResumeLayout(false);
        }

        public void restoreUISettings()
        {
            // restore the settings
            welcomeUserControl1.m_showWelcomTabAtStartup.Checked = RollbackUI.Default.ShowWelcomeTabAsStartTab;
            if (!RollbackUI.Default.ShowWelcomeTabAsStartTab)
            {
                m_explorerTabControl.SelectedTab = m_backupViewTab;
                m_splitContainer.Panel2Collapsed = false;
                int height = RollbackUI.Default.ExplorerAndBackupJobsSplitter2Height;
                if (height > m_splitContainer.Panel1MinSize &&
                    height < m_splitContainer.Panel2MinSize)
                {
                    m_splitContainer.SplitterDistance = RollbackUI.Default.ExplorerAndBackupJobsSplitter2Height;
                }
                else
                {
                    log.Warn("Invalid splitter distance <" + height + "> Panel1MinSize=" + m_splitContainer.Panel1MinSize + " Panel2MinSize=" + m_splitContainer.Panel2MinSize);
                }
            }

        }

        /// <summary>
        /// Add the job history tab
        /// </summary>
        private void addJobHistoryTab()
        {
            m_jobHistoryViewer = new JobHistoryViewer();
            m_jobHistoryViewer.Dock = DockStyle.Fill;

            m_jobHistoryTab = new TabPage();
            m_jobHistoryTab.ToolTipText = "History of all the backup jobs";
            m_jobHistoryTab.Text = "Backup History";
            m_jobHistoryTab.Controls.Add(m_jobHistoryViewer);
            m_jobHistoryTab.ImageIndex = ImageResources.Indexes.m_historicalGraphImage;
            this.m_explorerTabControl.Controls.Add(m_jobHistoryTab);
        }

        /// <summary>
        /// Attempt to shutdown all the threads.
        /// Return false if the attempt was unsuccessful.  A second attempt to
        /// call this function will forcibly shutdown all the threads.
        /// </summary>
        internal bool shutdownThreads()
        {
            if (m_log4NetAppender != null)
            {
                // no more capturing of logs in the ui because of thread contention.
                m_log4NetAppender.m_disabled = true;
            }
            bool success = m_backupRestoreRunner.stopAllThreads();
            return success;
        }

        internal void shutdown()
        {
            if (m_log4NetAppender != null)
            {
                // no more capturing of logs in the ui because of thread contention.
                m_log4NetAppender.m_disabled = true;
            }
            // attempt to shutdown the scheduler thread first.
            m_backupSetComposite.shutdown();
            if (m_jobHistoryViewer != null)
            {
                string path = m_backupRestoreRunner.getRootFileSavePath();
                m_jobHistoryViewer.saveHistoryData(path);
            }
            RollbackUI.Default.ShowWelcomeTabAsStartTab = welcomeUserControl1.m_showWelcomTabAtStartup.Checked;
            RollbackUI.Default.ExplorerAndBackupJobsSplitter2Height = m_splitContainer.SplitterDistance;
        }

        /// <summary>
        /// Callback when the explorertab changes;
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tabControl1_Selected(object sender, TabControlEventArgs e)
        {
            TabControl tab = (TabControl)sender;

            BackupJob job = m_backupSetComposite.getSelectedBackupJob();

            if (tab!=null && tab.SelectedTab != null) 
            {
                this.m_splitContainer.Panel2Collapsed = false;
                if (job != null)
                {
                    if ((tab.SelectedTab == m_restoreViewTab) && (m_restoreViewData != null))
                    {
                        log.Debug("Switching to RestoreViewTab for Job " + job.GetKey());
                        updateRestoreView();
                        m_backupSetComposite.switchToRestoreListView();
                    }
                    else if (tab.SelectedTab == m_backupViewTab)
                    {
                        log.Debug("Switching to BackupViewTab for Job " + job.GetKey());
                        m_backupSetComposite.switchtoBackupListView();
                    }
                }
                
                if (tab.SelectedTab == m_jobHistoryTab || 
                    tab.SelectedTab == m_logMessageViewerTab || 
                    tab.SelectedTab == m_welcomeTab)
                {
                    this.m_splitContainer.Panel2Collapsed = true;
                }

            }

        }

        /// <summary>
        /// Update the restoreview data with the currently selected tab data.
        /// </summary>
        private void updateRestoreView()
        {
            BackupJobTab jobTab = m_backupSetComposite.getSelectedBackupTab();
            if (jobTab != null)
            {
                BackupJob job = jobTab.getBackupJob();
                string name = job.m_Id.m_Name;
                if (!name.Equals(m_restoreViewData.RootDisplayNode, StringComparison.InvariantCultureIgnoreCase))
                {
                    m_restoreViewData.RootDisplayNode = name;
                    m_restoreViewData.RootDisplayNodePath = name + "\\";
                    m_restoreViewData.m_restoreDir = job.getFinalDestinationDir();
                    m_restoreViewPanel.RefreshRootNode(2);
                }
            }
        }

        /// <summary>
        /// Callback from BackpJobTab when the selected tab changes
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backupJobTabSelectionEventHandler(object sender, TabControlEventArgs e)
        {
            if (e.TabPageIndex >= 0) 
            {
                TabPage selectedTab = this.m_explorerTabControl.SelectedTab;
                if ( selectedTab == m_restoreViewTab && (m_restoreViewData != null))
                {
                    updateRestoreView();
                }
            }
        }

        internal int LoadSavedBackupSets(string file)
        {
            int count = m_backupSetComposite.loadSavedBackupSets(file);
            return count;
        }

        /// <summary>
        /// Update the UI with the status of the current operation
        /// </summary>
        /// <param name="param">This is the backupJobTab object</param>
        /// <param name="statistics">This is the current statistics</param>
        public void statusUpdateHandler(BackupInfo bInfo)
        {
            MethodInvoker method = delegate()
            {
                this.statusUpdateHandlerDelegated(bInfo);
            };
            try
            {
                if (m_handleCreated)
                {
                    this.Invoke(method);
                }
            }
            catch (Exception e)
            {
                string msg = "Caught an exception while calling statusUpdateHandler.  Backup operation will be stopped";
                log.Error(msg, e);
                AppUI.ShowException(msg, e);
                m_backupRestoreRunner.stopCurrentBackup();
            }
        }

        private void statusUpdateHandlerDelegated(BackupInfo bInfo)
        {
            BackupStatistics statistics = bInfo.m_Statistics;
            CurrentOperation operation = statistics.m_operation;
            BackupJob job = bInfo.m_Job;
            string msg = statistics.m_operationMsg;

            BackupJobTab tab = null;
            if (job != null)
            {
                tab = (BackupJobTab)bInfo.m_Job.getData();
            }

            if (tab != null)
            {
                string oprMsgWithJobName = "Job <" + job.m_Id.m_Name + ">  ";

                if (msg != null)
                {
                    oprMsgWithJobName += msg.Replace("\r\n", ",   ");
                }

                // update the tabs and the buttons in the tab.
                tab.statusUpdateHandler(bInfo);
                string jobKey = job.m_Id.m_Name + "         Guid=" + job.m_Id.m_Guid;
                switch (operation)
                {
                    case CurrentOperation.SystemError:
                    case CurrentOperation.AccessDenied:
                    case CurrentOperation.PruneException:
                        showFileError(jobKey, msg, ref m_showExceptions);
                        break;
                    case CurrentOperation.Finished:
                        m_showExceptions = false;
                        if (m_jobHistoryTab != null)
                        {
                            bInfo.m_Statistics.m_lastRunSuccess = bInfo.m_Job.m_lastRunSuccess;
                            m_jobHistoryViewer.addJobToHistory(bInfo.m_Statistics);
                        }
                        {
                            // create an anonymous delegate and add it to the restore runner thread to be executed later.
                            BackupRestoreRunner.Task task = delegate()
                            {
                                sendJobFinishedEmail(bInfo);
                            };
                            m_backupRestoreRunner.addTask(task);
                        }

                        if (bInfo.isInteractiveBackup( ))
                        {
                            JobFinishedDialog finished = new JobFinishedDialog();
                            finished.setStatus(msg, bInfo);
                            finished.ShowDialog();
                        }

                        if (bInfo.isDeleteOperation() && bInfo.m_Statistics.m_backupWasStopped == false)
                        {
                            // This was a delete operation hence remove the backup job at the end
                            m_backupRestoreRunner.m_JobManager.removeBackupJob(tab.getBackupJob());
                            m_backupSetComposite.removeBackupJobTab(tab);
                        }
                        break;
                    default:
                        if (bInfo.m_LastException != null)
                        {
                            if (!bInfo.m_LastExceptionDisplayed)
                            {
                                bInfo.m_LastExceptionDisplayed = true;
                                AppUI.ShowException(bInfo.m_Statistics.m_operationMsg, bInfo.m_LastException,ref m_showExceptions);
                            }
                        }
                        break;
                }
            }
            else if (operation == CurrentOperation.ScheduledSave)
            {
                // save all the backup jobs.
                if (m_backupSetComposite.haveBackupJobsChanged())
                {
                    m_backupSetComposite.updateAndSaveAllBackupJobs();
                }
            }
            else
            {
                log.Warn("There is not BackupJobTab associated with job <" + bInfo.m_Job.GetKey() + ">");
            }
        }

        private void sendJobFinishedEmail(BackupInfo bInfo)
        {
            BackupJob job = bInfo.m_Job;
            if (job.m_EmailList.Count > 0)
            {
                String subject = "Backup Job '" + job.m_Id.m_Name + "' Finished";
                String reason = " ' for job '" + job.m_Id.m_Name + "'";
                BackupStatistics stats = bInfo.m_Statistics;
                String statsData = bInfo.createJobEndStatus();
                String itemList = bInfo.createItemList();
                String recentlyCreatedDirsList = bInfo.createRecentlyCreatedDirectoryList();
                String statsHtmlTable = bInfo.createStatsHtmlTable( );
                String fileTypeHtmlTable = bInfo.createFileTypesHtmlTable();
                    //"Files: Copied=" + stats.m_fileStats.m_copied + "       Skipped=" + stats.m_fileStats.m_skipped + "        Deleted=" + stats.m_fileStats.m_deleted + "\r\n"
                    //+ "Directories: Scanned=" + stats.m_dirStats.m_copied + "       Skipped=" + stats.m_dirStats.m_skipped + "       Created=" + stats.m_dirStats.m_created + "        Deleted=" + stats.m_dirStats.m_deleted;
                statsData =  statsData + 
                    "<BR><BR>" + 
                    itemList +
                    recentlyCreatedDirsList +
                    statsHtmlTable + 
                    "<BR><BR>" + 
                    fileTypeHtmlTable + 
                    "";
                log.Info("Emailing: " + statsData);
                foreach (String emailId in job.m_EmailList)
                {
                    try
                    {
                        EmailAndSmtpTable.SendEmail(subject, statsData, reason, emailId);
                    }
                    catch (Exception e)
                    {
                        log.Warn("Could not send email.", e);
                        if (bInfo.isInteractiveBackup())
                        {
                            AppUI.ShowException("Could not send email.", e);
                        }
                    }
                }
            }
            else
            {
                log.Info("No email id selected for end of job notification");
            }
        }

        /// <summary>
        /// show file error dialog. Stop the backup operation if the user chooses so.
        /// </summary>
        /// <param name="p"></param>
        /// <param name="msg"></param>
        /// <param name="m_showSystemErrorDialog"></param>
        private void showFileError(string jobKey, string msg, ref bool show)
        {
            DialogResult result = m_fileErrorsDialog.showFileError(jobKey, msg, ref show);
            if (result == DialogResult.Abort)
            {
                m_backupRestoreRunner.stopCurrentBackup();
            }
        }

        /// <summary>
        /// Handle start button click via a callback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void startButtonClick(object sender, EventArgs e)
        {
            BackupJob job = m_backupSetComposite.getSelectedBackupJob();
            if (job != null)
            {
                if (job.isRunning())
                {
                    m_backupSetComposite.pauseButtonClick();
                }
                else
                {
                    startBackupJob(job);
                }
            }
        }

        private void startBackupJob(BackupJob job)
        {
            m_showExceptions = true;
            try
            {
                m_backupSetComposite.updateAndSaveAllBackupJobs();
            }
            catch (Exception ex)
            {
                log.Warn("Could not save backup job", ex);
                AppUI.ShowException("Could not save current job", ex);
            }
            do
            {
                try
                {
                    if (m_backupRestoreRunner.isRunning())
                    {
                        log.Info("Backup is already running");
                        AppUI.ShowMessage("A Backup is already running");
                        break;
                    }
                    // check to see if the job is enabled
                    if (!job.m_Enabled)
                    {
                        DialogResult result = AppUI.ShowMessage("This Backup job is currently disabled.  Do you want to enable it?", MessageBoxButtons.YesNoCancel);
                        if (result == DialogResult.Yes)
                        {
                            job.m_Enabled = true;
                        }
                        else
                        {
                            log.Info("Jobs was cancelled by user action.  User did not want to re-enable this job.");
                            break;
                        }
                    }

                    BackupInfo.BackupType backupType =
                        (m_explorerTabControl.SelectedTab == m_restoreViewTab) ? BackupInfo.BackupType.RESTORE : BackupInfo.BackupType.BACKUP;

                    List<BackupItem> items = null;
                    // warn the user if the resotre check box is turned on
                    if (backupType == BackupInfo.BackupType.RESTORE)
                    {
                        DialogResult result = AppUI.ShowMessage(
                            "You have chosen to perform a resotre operation\r\n"
                            + "This will copy files and folders from your backup to the original location\r\n\r\n"
                            + "Do you want to continue?",
                            MessageBoxButtons.YesNo,
                            MessageBoxIcon.Warning);
                        if (result == DialogResult.No)
                        {
                            log.Info("Jobs was cancelled by user action.  User did not want to run job in restore mode.");
                            break;
                        }
                        else
                        {
                            items = job.m_RestorePathList;
                        }
                    }
                    else
                    {
                        items = job.m_BackupPathList;
                    }

                    string dest = job.getFinalDestinationDir();
                    BackupInfo binfo = new BackupInfo(items,
                        dest,
                        backupType,
                        job,
                        statusUpdateHandler
                        );
                    m_backupSetComposite.startButtonClick(binfo);
                }
                catch (Exception ex)
                {
                    log.Error("Caught an exception while starting backup", ex);
                    AppUI.ShowException(ex.Message, ex,ref m_showExceptions);
                }
            } while (false);
        }

        private void stopButtonClick(object sender, EventArgs e)
        {
            m_backupSetComposite.stopButtonClick( );
        }

        private void pauseButtonClick(object sender, EventArgs e)
        {
            m_backupSetComposite.pauseButtonClick( );
        }

        private void advancedButtonClick(object sender, System.EventArgs e)
        {
            m_backupSetComposite.advancedButtonClick(sender, e);
        }

        private void dragDropHandler(object sender, System.EventArgs e)
        {
            m_backupSetComposite.updateAndSaveAllBackupJobs();
        }

        /// <summary>
        /// Add the restore tab
        /// </summary>
        private void addRestoreTab()
        {
            m_restoreViewData = new RestoreData();

            m_restoreViewTab = new TabPage();
            m_restoreViewTab.ToolTipText = "Switch to Restore View to start restoring your backups";
            m_restoreViewTab.Text = "Restore Your Backup";
            m_restoreViewPanel = new Explorer2PaneView();
            m_restoreViewPanel.setExplorerData(m_restoreViewData);
            m_restoreViewPanel.Dock = DockStyle.Fill;
            m_restoreViewTab.Controls.Add(m_restoreViewPanel);
            m_restoreViewTab.ImageIndex = ImageResources.Indexes.m_restoreBackupImage;
            this.m_explorerTabControl.Controls.Add(m_restoreViewTab);
        }

        /// <summary>
        /// Add the log message viewer
        /// </summary>
        private void addLogMessageViewerTab()
        {
            this.SuspendLayout();
            m_logMessageViewerTab = new TabPage();
            m_logMessageViewerTab.ToolTipText = "View important log messages generated by the application.";
            m_logMessageViewerTab.Text = "Message Viewer";
            m_logMessageViewer = new LogMessageViewer();
            m_logMessageViewer.Dock = DockStyle.Fill;
            m_logMessageViewerTab.Controls.Add(m_logMessageViewer);
            m_logMessageViewerTab.ImageIndex = ImageResources.Indexes.m_logMessageImage;
            m_explorerTabControl.Controls.Add(m_logMessageViewerTab);
            // connect the appender and the log message viewer
            try
            {
                Hierarchy h = LogManager.GetRepository() as Hierarchy;
                m_log4NetAppender = h.Root.GetAppender("RollbackAppender") as RollbackAppender;
                if (m_log4NetAppender != null)
                {
                    m_log4NetAppender.m_LogMessageViewer = m_logMessageViewer;
                }
            }
            catch (Exception e)
            {
                log.Error("Error loading RollbackAppender.  LogMessageViewer will not work.", e);
            }
            this.ResumeLayout(false);
        }

        private void setExplorerViewData()
        {
            var explorerData = new ExplorerData();
            explorerData.RootDisplayNode = "MyComputer";
            explorerData.RootDisplayNodePath = explorerData.RootDisplayNode + "\\";
            explorerData.m_leftData.m_allowDrag = true;
            explorerData.m_leftData.m_allowDrop = false;
            explorerData.m_rightData.m_allowDrag = true;
            explorerData.m_rightData.m_allowDrop = false;
            explorerData.m_rightData.m_itemsChecked = false;
            explorerData.m_rightData.m_checkBoxes = false;
            this.m_explorerTopPanel.setExplorerData(explorerData);
        }


        internal void processClearButton()
        {
            if (m_explorerTabControl.SelectedTab == m_jobHistoryTab)
            {
                m_jobHistoryViewer.clearHistory();
            }
            else if (m_explorerTabControl.SelectedTab == m_logMessageViewerTab)
            {
                m_logMessageViewer.clearLogMessages();
            }
        }

        internal void createNewBackupJob()
        {
            m_backupSetComposite.newBackupJob();
        }

        /// <summary>
        /// Get the path of the currently loaded backup profile xml file.
        /// </summary>
        /// <returns></returns>
        internal string getCurrentBackupProfile()
        {
            string str = m_backupRestoreRunner != null ? m_backupRestoreRunner.m_JobManager.m_RootPath : null;
            return str;
        }

        internal void closeBackupJob()
        {
            BackupJobTab tab = m_backupSetComposite.getSelectedBackupTab();
            if (tab != null)
            {
                if (m_backupRestoreRunner.isRunning())
                {
                    AppUI.ShowMessage("Backup Job is currently running.  Cannot remove any BackupJob set");
                }
                else
                {
                    BackupJob job = tab.getBackupJob();
                    DeleteJobDialog delDialog = new DeleteJobDialog();
                    string question =
                        "Are you sure you want to remove Backup Set '" + job.m_Id.m_Name + "'"
                        + "\r\nBackup Path is <" + job.m_DestDir + ">";
                    if (DialogResult.OK == delDialog.showDeleteJobDialog(question))
                    {
                        if (delDialog.m_deleteBackedUpDataCheckbox.Checked)
                        {
                            List<BackupItem> items = job.m_BackupPathList;
                            string destPath = job.getFinalDestinationDir();
                            BackupInfo binfo = new BackupInfo(items,
                                destPath,
                                BackupInfo.BackupType.DELETE_BACKUP,
                                job,
                                statusUpdateHandler
                                );
                            m_backupSetComposite.startButtonClick(binfo);
                        }
                        else
                        {
                            m_backupRestoreRunner.m_JobManager.removeBackupJob(tab.getBackupJob());
                            m_backupSetComposite.removeBackupJobTab(tab);
                        }
                    }
                }
            }
        }

        internal void afterShown()
        {
            try
            {
                m_handleCreated = true;
                log.Info("Window handle has been created");
                setExplorerViewData();
                createOtherTabs();
            }
            catch (Exception e)
            {
                AppUI.LogAndShowException("Could not start scheduler thread", e);
            }
        }
    }
}
